# -*- coding: utf-8 -*-

import base64
import httplib
import logging
import socket
from lxml import etree
from urllib import urlencode
from urlparse import urlparse
from pprint import pformat
from httplib import HTTPConnection

log = logging.getLogger("HTTP Test Suite")

class HTTPConnectionException(Exception):
    def __init__(self,msg,url):
        Exception.__init__(self,msg)
        self.url = url

class HTTPResponseException(Exception):
    """Clase de exceptiones HTTP. Soy levantado cuando hay problemas no esperados con la petici�n HTTP."""
    def _getXML(self,s):
        """ retorma el texto del XML que entrega CustodiaNG al reportar un error """
        xmlPosition = s.find("<?xml")
        if xmlPosition >= 0:
            return etree.tostring(etree.fromstring(s[xmlPosition:]),pretty_print=True)
        return ""

    def __init__(self,msg,method=None,uri=None,response=None,content=None,headers=None):
        """Constructor de la excepcion."""
        Exception.__init__(self,msg)
        self.method = method
        self.uri    = uri
        self.response = response
        self.status  = response["status"]
        try:
            self.status_code = int(self.status)
        except ValueError:
            self.status_code = 0
        self.content = content
        self.headers = headers
        self.message =  pformat(vars(self))
        self.xml = self._getXML(self.content)

        # Deja en self.errorDescription la informacion del problema generado
        try:
            self.etree = etree.fromstring(self.xml)
            self.errorDescription = "%s: %s" % (self.etree.xpath("//exception/text()")[0],self.etree.xpath("//description/text()")[0])
        except:
            self.etree = None
            self.errorDescription = self.content

class RestHTTP(object):
    def __init__(self):
        """Objeto cual habla HTTP con el servidor"""
        self.credentials    = None

    def authenticate(self,user, password):
        """Setea los credenciales para la autenticaci�n
        @param user: Nombre de la cuenta cual se conecta al servicio.
        @param password: Contrase�a del usuario
        """
        self.credentials = "Basic %s" % (base64.encodestring("%(user)s:%(password)s"% locals()).strip())

    def request(self, uri, method, body=None, headers=None, expected_status=200, warning_status=None, return_stream=False):
        """Request HTTP generico
        @param uri: URL cual se llama via HTTP
        @param method: Methodo HTTP
        @param body: Cuerpo enviado en el caso de POST o PUT.
        @param headers: Los encabezados para mandar con la petici�n.
        @type headers: dict
        @param expected_status: Estado esperado de respuesta.
        @return: (response, content)
        """
        log.debug("%s, %s" % (method,uri))
        if type(body).__name__ == 'dict':
            body = urlencode(body)
        uriparts = urlparse(uri)
        self.httpConn = HTTPConnection(uriparts.hostname,port=uriparts.port)
        relative_url = uriparts.path
        if uriparts.query:
            relative_url = "%s?%s" % (uriparts.path, uriparts.query)
        if not headers:
            headers = {}
        if self.credentials:
            headers['Authorization'] = self.credentials
        try:
            self.httpConn.request(method, relative_url, body, headers)
        except (socket.error), e:
            raise HTTPConnectionException("Unavailable service %s" % uri, uri)
        httpResp = self.httpConn.getresponse()
        resp = dict(httpResp.getheaders())
        resp['status'] = httpResp.status
        if return_stream:
            content = httpResp
        else:
            content = httpResp.read()

        if warning_status and httpResp.status!=int(warning_status):
            log.warning(resp)
        elif expected_status and int(resp["status"])!=int(expected_status):
            raise HTTPResponseException("Unexpected response status for URL %s. Expected = %s,\nReceived:\n%s" %
                                 ( uri, expected_status, resp["status"] ),
                                  method,uri,resp,content,headers)
        return resp, content

    def GET(self, uri, body=None, headers=None, expected_status=200,warning_status=None, return_stream=False):
        return self.request(uri,method="GET",body=body,headers=headers,expected_status=expected_status,warning_status=warning_status, return_stream=return_stream)

    def POST(self, uri, body, headers=None, expected_status=None,warning_status=None):
        return self.request(uri,method="POST",body=body,headers=headers,expected_status=expected_status,warning_status=warning_status)

    def PUT(self, uri, body, headers=None, expected_status=None,warning_status=None):
        return self.request(uri,method="PUT",body=body,headers=headers,expected_status=expected_status,warning_status=warning_status)

    def DELETE(self, uri, body=None, expected_status=None,warning_status=None, headers=None):
        return self.request(uri,method="DELETE",body=body,headers=headers,expected_status=expected_status,warning_status=warning_status)
